package utils

import (
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

// LogRotator 日志轮转器
type LogRotator struct {
	LogDir     string
	MaxSize    int64 // 字节
	MaxAge     int   // 天数
	MaxBackups int   // 备份文件数量
	Compress   bool  // 是否压缩
}

// NewLogRotator 创建新的日志轮转器
func NewLogRotator(logDir string, maxSize int64, maxAge, maxBackups int, compress bool) *LogRotator {
	return &LogRotator{
		LogDir:     logDir,
		MaxSize:    maxSize,
		MaxAge:     maxAge,
		MaxBackups: maxBackups,
		Compress:   compress,
	}
}

// LogFile 日志文件信息
type LogFile struct {
	Name     string
	Path     string
	ModTime  time.Time
	Size     int64
	IsActive bool
}

// RotateIfNeeded 如果需要则轮转日志
func (lr *LogRotator) RotateIfNeeded(currentLogPath string) error {
	// 检查当前日志文件大小
	info, err := os.Stat(currentLogPath)
	if err != nil {
		return nil // 文件不存在，不需要轮转
	}

	if info.Size() < lr.MaxSize {
		return nil // 文件大小未超限，不需要轮转
	}

	return lr.RotateLog(currentLogPath)
}

// RotateLog 轮转指定日志文件
func (lr *LogRotator) RotateLog(currentLogPath string) error {
	// 生成轮转后的文件名
	rotatedPath := lr.generateRotatedPath(currentLogPath)

	// 重命名当前日志文件
	if err := os.Rename(currentLogPath, rotatedPath); err != nil {
		return fmt.Errorf("重命名日志文件失败: %w", err)
	}

	// 如果启用压缩，压缩轮转后的文件
	if lr.Compress {
		if err := lr.compressFile(rotatedPath); err != nil {
			// 压缩失败不影响轮转过程，只记录错误
			fmt.Printf("压缩日志文件失败: %v\n", err)
		}
	}

	// 清理旧文件
	if err := lr.cleanupOldFiles(); err != nil {
		fmt.Printf("清理旧日志文件失败: %v\n", err)
	}

	return nil
}

// generateRotatedPath 生成轮转后的文件路径
func (lr *LogRotator) generateRotatedPath(currentPath string) string {
	dir := filepath.Dir(currentPath)
	name := filepath.Base(currentPath)
	ext := filepath.Ext(name)
	nameWithoutExt := strings.TrimSuffix(name, ext)

	// 添加时间戳
	timestamp := time.Now().Format("20060102150405")
	rotatedName := fmt.Sprintf("%s.%s%s", nameWithoutExt, timestamp, ext)

	return filepath.Join(dir, rotatedName)
}

// compressFile 压缩文件
func (lr *LogRotator) compressFile(filePath string) error {
	// 打开源文件
	sourceFile, err := os.Open(filePath)
	if err != nil {
		return fmt.Errorf("打开源文件失败: %w", err)
	}
	defer sourceFile.Close()

	// 创建压缩文件
	compressedPath := filePath + ".gz"
	compressedFile, err := os.Create(compressedPath)
	if err != nil {
		return fmt.Errorf("创建压缩文件失败: %w", err)
	}
	defer compressedFile.Close()

	// 创建gzip写入器
	gzipWriter := gzip.NewWriter(compressedFile)
	defer gzipWriter.Close()

	// 复制数据
	if _, err := io.Copy(gzipWriter, sourceFile); err != nil {
		os.Remove(compressedPath) // 清理失败的压缩文件
		return fmt.Errorf("压缩数据失败: %w", err)
	}

	// 删除原文件
	if err := os.Remove(filePath); err != nil {
		return fmt.Errorf("删除原文件失败: %w", err)
	}

	return nil
}

// cleanupOldFiles 清理旧文件
func (lr *LogRotator) cleanupOldFiles() error {
	logFiles, err := lr.getLogFiles()
	if err != nil {
		return fmt.Errorf("获取日志文件列表失败: %w", err)
	}

	// 按修改时间排序（最新的在前面）
	sort.Slice(logFiles, func(i, j int) bool {
		return logFiles[i].ModTime.After(logFiles[j].ModTime)
	})

	// 清理超过数量限制的文件
	if lr.MaxBackups > 0 && len(logFiles) > lr.MaxBackups {
		for _, file := range logFiles[lr.MaxBackups:] {
			if !file.IsActive {
				if err := os.Remove(file.Path); err != nil {
					fmt.Printf("删除备份文件失败 %s: %v\n", file.Path, err)
				}
			}
		}
	}

	// 清理超过时间限制的文件
	if lr.MaxAge > 0 {
		cutoff := time.Now().AddDate(0, 0, -lr.MaxAge)
		for _, file := range logFiles {
			if !file.IsActive && file.ModTime.Before(cutoff) {
				if err := os.Remove(file.Path); err != nil {
					fmt.Printf("删除过期文件失败 %s: %v\n", file.Path, err)
				}
			}
		}
	}

	return nil
}

// getLogFiles 获取日志文件列表
func (lr *LogRotator) getLogFiles() ([]LogFile, error) {
	entries, err := os.ReadDir(lr.LogDir)
	if err != nil {
		return nil, fmt.Errorf("读取日志目录失败: %w", err)
	}

	var logFiles []LogFile
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}

		name := entry.Name()

		// 检查是否为日志文件（包括压缩文件）
		if !lr.isLogFile(name) {
			continue
		}

		info, err := entry.Info()
		if err != nil {
			continue
		}

		logFile := LogFile{
			Name:     name,
			Path:     filepath.Join(lr.LogDir, name),
			ModTime:  info.ModTime(),
			Size:     info.Size(),
			IsActive: lr.isActiveLogFile(name),
		}

		logFiles = append(logFiles, logFile)
	}

	return logFiles, nil
}

// isLogFile 检查是否为日志文件
func (lr *LogRotator) isLogFile(filename string) bool {
	// 检查 .log 文件或 .log.gz 文件
	if strings.HasSuffix(filename, ".log") {
		return strings.HasPrefix(filename, "dcarpai-guider-")
	}
	if strings.HasSuffix(filename, ".log.gz") {
		baseName := strings.TrimSuffix(filename, ".gz")
		return strings.HasPrefix(baseName, "dcarpai-guider-") && strings.HasSuffix(baseName, ".log")
	}
	return false
}

// isActiveLogFile 检查是否为当前活跃的日志文件
func (lr *LogRotator) isActiveLogFile(filename string) bool {
	// 活跃的日志文件格式: dcarpai-guider-2006010215.log
	// 轮转的日志文件格式: dcarpai-guider-2006010215.20060102150405.log
	if !strings.HasPrefix(filename, "dcarpai-guider-") {
		return false
	}

	// 移除前缀和后缀
	name := strings.TrimPrefix(filename, "dcarpai-guider-")
	name = strings.TrimSuffix(name, ".log")
	name = strings.TrimSuffix(name, ".gz")

	// 活跃文件应该只有10位数字（YYYYMMDDHH）
	// 轮转文件会有更多字符（包含时间戳分隔符）
	if len(name) == 10 {
		// 检查是否全为数字
		for _, r := range name {
			if r < '0' || r > '9' {
				return false
			}
		}
		return true
	}

	return false
}

// GetStats 获取轮转器统计信息
func (lr *LogRotator) GetStats() (map[string]interface{}, error) {
	logFiles, err := lr.getLogFiles()
	if err != nil {
		return nil, err
	}

	var totalSize int64
	var activeFiles, backupFiles int

	for _, file := range logFiles {
		totalSize += file.Size
		if file.IsActive {
			activeFiles++
		} else {
			backupFiles++
		}
	}

	stats := map[string]interface{}{
		"log_dir":      lr.LogDir,
		"total_files":  len(logFiles),
		"active_files": activeFiles,
		"backup_files": backupFiles,
		"total_size":   totalSize,
		"config": map[string]interface{}{
			"max_size":    lr.MaxSize,
			"max_age":     lr.MaxAge,
			"max_backups": lr.MaxBackups,
			"compress":    lr.Compress,
		},
	}

	return stats, nil
}

// ForceRotate 强制轮转指定的日志文件
func (lr *LogRotator) ForceRotate(logFilePath string) error {
	if _, err := os.Stat(logFilePath); err != nil {
		return fmt.Errorf("日志文件不存在: %w", err)
	}

	return lr.RotateLog(logFilePath)
}
